#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unordered_map>
#include "protocol.hpp"
#include "sock.hpp"
#include "log.hpp"
#include "connect.hpp"
#include "epoller.hpp"
#include "util.hpp"

namespace reactor
{

    class TcpServer
    {
        const static int default_num = 64;

    public:
        TcpServer(func_t serve, int port)
            : _port(port), _rcvs(nullptr), _num(default_num), _serve(serve)
        {
        }
        ~TcpServer()
        {
        }
        void InitServer()
        {
            logMessage(DEBUG, "initServer start");
            // 1.创建套接字  取消time_wait状态的绑定冷却时间
            _listen.Socket();
            int opt = 1;
            setsockopt(_listen.fd(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
            _listen.Bind(_port);
            _listen.Listen();

            // 2.创建epoll模型
            _epoller.create();

            // 3.创建listen连接数据结构，并加入到epoll模型中，在添加到unordered_map连接池中
            addConnection(_listen.fd(), EPOLLIN | EPOLLET,
                          std::bind(&TcpServer::accepter, this, std::placeholders::_1), nullptr, nullptr);

            // 4.初始化epoll_wait获取的连接
            _rcvs = new struct epoll_event[_num];

            logMessage(DEBUG, "initServer success");
        }

        void addConnection(int sock, uint32_t events, func_t recver, func_t sender, func_t excpeter)
        {
            // 0.检查是否需要设计非阻塞
            if (events & EPOLLET)
            {
                Util::SetNonBlock(sock);
            }
            // 1.创建连接数据结构
            Connection *con = new Connection(this, sock);
            con->Register(recver, sender, excpeter);

            // 2.将连接"写透式"添加到epoll模型中
            _epoller.ctl(sock, EPOLL_CTL_ADD, events);

            // 3.把连接数据结构放进哈希表
            _cons[sock] = con;
        }
        void accepter(Connection *con)
        {
            // logMessage(DEBUG, "accepter in");
            while (1)
            {
                std::string clientip;
                uint16_t clientport;
                // logMessage(DEBUG, "accept start");
                int sock = _listen.Accept(&clientip, &clientport);
                // logMessage(DEBUG, "accept end");

                if (sock > 0)
                {
                    logMessage(DEBUG, "accept a new link ,clientip: %s, clientpoet: %d", clientip.c_str(), clientport);

                    addConnection(sock, EPOLLIN | EPOLLET, std::bind(&TcpServer::recver, this, std::placeholders::_1),
                                  std::bind(&TcpServer::sender, this, std::placeholders::_1),
                                  std::bind(&TcpServer::excepter, this, std::placeholders::_1));
                    logMessage(DEBUG, "add a new connection to epoller and unordered_map");
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        logMessage(DEBUG, "accept end");
                        return;
                    }
                    else if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        if (con->_excepter)
                            con->_excepter(con);
                        return;
                    }
                }
            }
        }

        void recver(Connection *con)
        {
            // logMessage(DEBUG, "recver in");
            char buffer[1024];
            while (true)
            {
                int n = recv(con->_sock, buffer, sizeof(buffer) - 1, 0);
                if (n > 0)
                {
                    buffer[n] = 0;
                    con->_inbuffer += buffer;
                    logMessage(DEBUG, "%d -> %s", con->_sock, con->_inbuffer.c_str());
                }
                else if (n == 0)
                {
                    if (con->_excepter)
                        con->_excepter(con);
                    return;
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        break;
                    }
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (con->_excepter)
                            con->_excepter(con);
                        return;
                    }
                }
            }
            std::cout << "server start" << std::endl;
            _serve(con);
            // std::string out;
            // while(recvPackage(con->_sock, con->_inbuffer, &out))
            // {
            //     con->_outbuffer += _handler(out);
            // }
        }
        void sender(Connection *con)
        {
            while (true)
            {
                ssize_t n = send(con->_sock, con->_outbuffer.c_str(), con->_outbuffer.size(), 0);
                if (n > 0)
                {
                    con->_outbuffer.erase(0, n);
                    if (con->_outbuffer.empty())
                        break;
                }
                else if (n == 0)
                {
                    if (con->_excepter)
                        con->_excepter(con);
                    return;
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    if (errno == EINTR)
                        continue;

                    else
                    {
                        if (con->_excepter)
                            con->_excepter(con);
                        return;
                    }
                }
            }

            // 如果没有写完，将写事件推入epoll模型
            if (!con->_outbuffer.empty())
                con->_tsvp->enableReadWrite(con, true, true);
            else
                con->_tsvp->enableReadWrite(con, true, false);
        }

        void excepter(Connection *con)
        {
            // 1.从epoller模型中去除
            _epoller.ctl(con->_sock, EPOLL_CTL_DEL, 0);

            // 2.从sock与缓冲区等的内核数据中去除
            _cons.erase(con->_sock);

            // 3.释放连接数据结构(析构关闭对应的文件描述符)
            con->Close();
            logMessage(DEBUG, "关闭文件描述符: %d", con->_sock);
            delete con;

        }

        bool isConnectionExist(int sock)
        {
            auto pos = _cons.find(sock);
            return pos != _cons.end();
        }
        void loop(int timeout)
        {
            int n = _epoller.wait(_rcvs, _num, timeout);
            for (int i = 0; i < n; ++i)
            {
                int sock = _rcvs[i].data.fd;
                uint32_t events = _rcvs[i].events;

                // 将等待错误读写交付，交给上层处理
                if (events & EPOLLHUP)
                    events |= EPOLLIN | EPOLLOUT;
                if (events & EPOLLERR)
                    events != EPOLLIN | EPOLLOUT;

                if ((events & EPOLLIN) && isConnectionExist(sock) && _cons[sock]->_recver)
                {
                    std::cout << "read ready" << std::endl;
                    _cons[sock]->_recver(_cons[sock]);
                }

                if ((events & EPOLLOUT) && isConnectionExist(sock) && _cons[sock]->_sender)
                {
                    _cons[sock]->_sender(_cons[sock]);
                }
            }
        }

        void enableReadWrite(Connection *con, bool isRead, bool isWrite)
        {
            uint32_t event = (isRead ? EPOLLIN : 0) | (isWrite ? EPOLLOUT : 0) | EPOLLET;

            _epoller.ctl(con->_sock, EPOLL_CTL_MOD, event);
        }
        // 事件派发器
        void depatch()
        {
            int timeout = -1;
            while (true)
            {
                loop(timeout);
                // logMessage(DEBUG, "timeout...");
            }
        }

    private:
        int _port;
        std::unordered_map<int, Connection *> _cons; // 把sock和对于的缓冲区等关联起来，为了epoll_wait后快速查找对应的缓冲区等
        Sock _listen;
        Epoller _epoller;
        struct epoll_event *_rcvs;
        int _num;
        func_t _serve;
    };

}
